/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace DaveSexton.DocProject.Sandcastle.TopicManagement
{
	internal sealed partial class InheritanceTopicFilterControl : UserControl, ITopicFilterEditor
	{
		#region Public Properties
		[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public InheritanceTopicFilter Filter
		{
			get
			{
				return boundFilter ?? new InheritanceTopicFilter(SelectedModes, Prefixes);
			}
		}

		[Category("Data"), DefaultValue(InheritanceTopicFilterModes.All)]
		public InheritanceTopicFilterModes SelectedModes
		{
			get
			{
				InheritanceTopicFilterModes modes = InheritanceTopicFilterModes.None;

				if (localCheckBox.Checked)
					modes |= InheritanceTopicFilterModes.Local;

				if (remoteCheckBox.Checked)
					modes |= InheritanceTopicFilterModes.Remote;

				if (prefixesCheckBox.Checked)
					modes |= InheritanceTopicFilterModes.Prefixes;

				return modes;
			}
			set
			{
				synchronizingModeCheckBoxes = true;

				try
				{
					noneCheckBox.Checked = value == InheritanceTopicFilterModes.None;

					localCheckBox.Checked = (value & InheritanceTopicFilterModes.Local) == InheritanceTopicFilterModes.Local;
					remoteCheckBox.Checked = (value & InheritanceTopicFilterModes.Remote) == InheritanceTopicFilterModes.Remote;
					prefixesCheckBox.Checked = (value & InheritanceTopicFilterModes.Prefixes) == InheritanceTopicFilterModes.Prefixes;
				}
				finally
				{
					synchronizingModeCheckBoxes = false;
				}
			}
		}

		[Category("Data"), DefaultValue(false)]
		public bool IncludePrefixes
		{
			get
			{
				return includePrefixesCheckBox.Checked;
			}
			set
			{
				includePrefixesCheckBox.Checked = value;
			}
		}

		[Category("Data")]
		public ICollection<string> Prefixes
		{
			get
			{
				return UnwrapPrefixes(boundPrefixes);
			}
			set
			{
				if (value == null)
					prefixesBindingSource.DataSource = null;
				else
					prefixesBindingSource.DataSource = boundPrefixes = WrapPrefixes(value);
			}
		}
		#endregion

		#region Private / Protected
		private bool synchronizingModeCheckBoxes;
		private InheritanceTopicFilter boundFilter;
		private DataTable boundPrefixes;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="InheritanceTopicFilterControl" /> class.
		/// </summary>
		public InheritanceTopicFilterControl()
		{
			InitializeComponent();

			prefixesDataGridView.AutoGenerateColumns = false;
		}
		#endregion

		#region Methods
		private void LoadFilter(InheritanceTopicFilter filter)
		{
			if (filter == null)
				throw new ArgumentNullException("filter");
			else if (boundFilter != null)
				throw new InvalidOperationException(Resources.Errors.InheritanceTopicFilterAlreadyBound);

			SelectedModes = filter.Modes;
			Prefixes = filter.Prefixes;
		}

		private void BindFilter(InheritanceTopicFilter filter)
		{
			if (filter == null)
			{
				DataBindings.Clear();

				SelectedModes = InheritanceTopicFilterModes.All;
				Prefixes = null;

				boundFilter = null;
			}
			else
			{
				DataBindings.Add("SelectedModes", filter, "Modes");
				DataBindings.Add("IncludePrefixes", filter, "IncludePrefixes");

				prefixesBindingSource.DataSource = boundPrefixes = WrapPrefixes(filter.Prefixes);

				boundFilter = filter;
			}
		}

		private static DataTable WrapPrefixes(ICollection<string> prefixes)
		{
			DataTable table = new DataTable();
			table.Locale = System.Globalization.CultureInfo.CurrentCulture;

			table.Columns.Add("Value", typeof(string));

			foreach (string value in prefixes)
				table.LoadDataRow(new object[] { value }, true);

			return table;
		}

		private static ICollection<string> UnwrapPrefixes(DataTable prefixes)
		{
			if (prefixes == null)
				return new List<string>(0).AsReadOnly();
			else
			{
				// NOTE: do not call prefixes.AcceptChanges() as it results in problems with the bound datagridview

				List<string> list = new List<string>(prefixes.Rows.Count);

				foreach (DataRow row in prefixes.Rows)
					if (row.RowState != DataRowState.Deleted && row.RowState != DataRowState.Detached && row[0] != DBNull.Value)
						list.Add((string) row[0]);

				return list.AsReadOnly();
			}
		}

		private void HandleModesCheckChanged(bool noneCheckChanged)
		{
			if (!synchronizingModeCheckBoxes)
			{
				synchronizingModeCheckBoxes = true;

				if (noneCheckChanged)
				{
					if (noneCheckBox.Checked)
					{
						localCheckBox.Checked = false;
						remoteCheckBox.Checked = false;
						prefixesCheckBox.Checked = false;
					}
					else
						// user cannot uncheck None if it's the only item checked
						noneCheckBox.Checked = true;
				}
				else
				{
					bool isItemChecked = false;

					foreach (Control control in modesTableLayoutPanel.Controls)
					{
						CheckBox checkBox = control as CheckBox;

						if (checkBox != null && checkBox != noneCheckBox && checkBox.Checked)
						{
							isItemChecked = true;
							break;
						}
					}

					noneCheckBox.Checked = !isItemChecked;
				}

				synchronizingModeCheckBoxes = false;
			}

			OnCanSaveChanged(new TopicFilterEditorTypeEventArgs(this.GetType()));
		}
		#endregion

		#region Event Handlers
		private void prefixesBindingSource_ListChanged(object sender, ListChangedEventArgs e)
		{
			if (boundFilter != null)
				boundFilter.Prefixes = UnwrapPrefixes(boundPrefixes);

			OnCanSaveChanged(new TopicFilterEditorTypeEventArgs(this.GetType()));
		}

		private void noneCheckBox_CheckedChanged(object sender, EventArgs e)
		{
			HandleModesCheckChanged(true);
		}

		private void LocalOrRemote_CheckChanged(object sender, EventArgs e)
		{
			if (localCheckBox.Checked && remoteCheckBox.Checked)
			{
				includePrefixesCheckBox.Enabled = false;
				includePrefixesCheckBox.Checked = true;
			}
			else if (!localCheckBox.Checked && !remoteCheckBox.Checked)
			{
				includePrefixesCheckBox.Enabled = false;
				includePrefixesCheckBox.Checked = false;
			}
			else
				includePrefixesCheckBox.Enabled = true;

			HandleModesCheckChanged(false);
		}

		private void prefixesCheckBox_CheckedChanged(object sender, EventArgs e)
		{
			prefixesGroupBox.Enabled = prefixesCheckBox.Checked;

			HandleModesCheckChanged(false);
		}

		private void prefixesDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
		{
			if (e.Exception is FormatException)
			{
				prefixesDataGridView[e.ColumnIndex, e.RowIndex].ErrorText = e.Exception.Message;
				e.Cancel = true;
			}
			else
				DocProjectEnvironment.ShowErrorDialog(e.Exception.Message);
		}

		private void prefixesDataGridView_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
		{
			string value = (string) e.FormattedValue;

			if (value.IndexOf(',') > -1)
			{
				prefixesDataGridView[e.ColumnIndex, e.RowIndex].ErrorText = Resources.Text.InheritanceTopicFilterPrefixNoComma;
				e.Cancel = true;
			}
			else
				prefixesDataGridView[e.ColumnIndex, e.RowIndex].ErrorText = null;
		}
		#endregion

		#region ITopicFilterEditor Members
		string ITopicFilterEditor.Title
		{
			get
			{
				return Resources.Text.InheritanceTopicFilter;
			}
		}

		[Browsable(false)]
		public bool CanApply
		{
			get
			{
				// The inheritance filter cannot be applied during topic management since it 
				// doesn't act on topics themselves, it acts on nested topic data only; i.e., 
				// there will never be any matches so it's pointless to enable the "apply" function.
				return false;
			}
		}

		[Browsable(false)]
		public bool CanSave
		{
			get
			{
				InheritanceTopicFilterModes modes = SelectedModes;

				return ((modes & InheritanceTopicFilterModes.Prefixes) == InheritanceTopicFilterModes.Prefixes && Prefixes.Count > 0)
					|| (modes & InheritanceTopicFilterModes.Local) == InheritanceTopicFilterModes.Local
					|| (modes & InheritanceTopicFilterModes.Remote) == InheritanceTopicFilterModes.Remote;
			}
		}

		ITopicFilter ITopicFilterEditor.Filter
		{
			get
			{
				return Filter;
			}
		}

		void ITopicFilterEditor.LoadFilter(ITopicFilter filter)
		{
			LoadFilter((InheritanceTopicFilter) filter);
		}

		void ITopicFilterEditor.BindFilter(ITopicFilter filter)
		{
			BindFilter((InheritanceTopicFilter) filter);
		}

		private readonly object CanApplyChangedEvent = new object();
		private readonly object CanSaveChangedEvent = new object();
		
		/// <summary>
		/// Event raised when the <see cref="CanSave"/> property value changes.
		/// </summary>
		[Category("Property Changed")]
		[Description("Event raised when the CanSave property value changes.")]
		public event EventHandler<TopicFilterEditorTypeEventArgs> CanSaveChanged
		{
			add
			{
				lock (CanSaveChangedEvent)
				{
					Events.AddHandler(CanSaveChangedEvent, value);
				}
			}
			remove
			{
				lock (CanSaveChangedEvent)
				{
					Events.RemoveHandler(CanSaveChangedEvent, value);
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="CanSaveChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="TopicFilterEditorTypeEventArgs" /> object that provides the arguments for the event.</param>
		private void OnCanSaveChanged(TopicFilterEditorTypeEventArgs e)
		{
			EventHandler<TopicFilterEditorTypeEventArgs> handler = null;

			lock (CanSaveChangedEvent)
			{
				handler = (EventHandler<TopicFilterEditorTypeEventArgs>) Events[CanSaveChangedEvent];
			}

			if (handler != null)
				handler(this, e);
		}

		/// <summary>
		/// Event raised when the <see cref="CanApply"/> property value changes.
		/// </summary>
		[Category("Property Changed")]
		[Description("Event raised when the CanApply property value changes.")]
		public event EventHandler<TopicFilterEditorTypeEventArgs> CanApplyChanged
		{
			add
			{
				lock (CanApplyChangedEvent)
				{
					Events.AddHandler(CanApplyChangedEvent, value);
				}
			}
			remove
			{
				lock (CanApplyChangedEvent)
				{
					Events.RemoveHandler(CanApplyChangedEvent, value);
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="CanApplyChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="TopicFilterEditorTypeEventArgs" /> object that provides the arguments for the event.</param>
		private void OnCanApplyChanged(TopicFilterEditorTypeEventArgs e)
		{
			EventHandler<TopicFilterEditorTypeEventArgs> handler = null;

			lock (CanApplyChangedEvent)
			{
				handler = (EventHandler<TopicFilterEditorTypeEventArgs>) Events[CanApplyChangedEvent];
			}

			if (handler != null)
				handler(this, e);
		}
		#endregion
	}
}
